[アップデート]新IAM Policy Condition aws:CalledVia を学ぶ
IAM PolicyのConditionにaws:CalledVia
というキーが追加されました。どういったキーなのかご説明します。
一言でいうと 「特定のサービス経由で実行している/いない」という権限付与の条件が設定可能になりました。
具体例を出して説明します。
これまで: aws:CalledVia
が無い世界
CloudFormation(以下CFn)を使って、VPCを作成したいとします。 CFnテンプレートは至極シンプルです。
AWSTemplateFormatVersion: "2010-09-09" Description: Create VPC Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: "192.168.0.0/16" EnableDnsSupport: "true" EnableDnsHostnames: "true" InstanceTenancy: default Tags: - Key: Name Value: "CalledViaTest-VPC"
まずは、CFnの実行権限しか無いユーザーでこのテンプレートを使ってCFnスタックを作成してみましょう。
実行結果です。 はい。当然失敗しますね。CFnの実行権限はあっても、VPCを作成する権限が無いからです。
というわけで、VPC作成権限も追加します。ec2:CreateVpc
だけで済むかと思ったのですが、タグを付与していたりなどで以下4つの権限が必要でした。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "hoge", "Effect": "Allow", "Action": [ "ec2:DescribeVpcs", "ec2:CreateVpc", "ec2:ModifyVpcAttribute", "ec2:createTags" ], "Resource": "*" } ] }
ですがここで一つ問題が発生します。 このユーザー、CFnを使わずコンソールで直接VPCを作成することもできちゃいます。
IaCを徹底したい場合など、このユーザーにはCFnでリソースを作成するのは許可しても、CFnを介さずリソースを作成することは許可したくない、といった場合、あると思います。それができないんですね。
※ 厳密には、この要件であれば(aws:CalledVia
を使わなくても)iam:PassRole
を使えば実現可能です。そのやり方についてはエントリ最下部補足欄に記載しましたのであとでご確認下さい。
これから: aws:CalledVia
がある世界
さて、ここでaws:CalledVia
Conditionキーです。先程のポリシーに追加します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "hoge", "Effect": "Allow", "Action": [ "ec2:DescribeVpcs", "ec2:CreateVpc", "ec2:ModifyVpcAttribute", "ec2:createTags" ], "Resource": "*", "Condition": { "ForAnyValue:StringEquals": { "aws:CalledVia": [ "cloudformation.amazonaws.com" ] } } } ] }
この権限でコンソールからVPCを作成しようとすると… はい。権限不足で失敗します。
CFn経由だと変わらず成功します!
Condition句をもうすこし詳しく解説
"Condition": { "ForAnyValue:StringEquals": { "aws:CalledVia": [ "cloudformation.amazonaws.com" ] } }
ForAnyValue
は、複数のメンバーからなる要素において、そのうち最低一つのメンバーがForAnyValue:
の後ろの判定条件で真になった場合に真という条件です。上記だとForAnyValue:StringEquals
なので、複数のメンバーからなる要素のうち最低一つのメンバーの値が一致している場合に真になります。aws:CalledVia
はリスト形式の値を取ります。なぜなら経由するサービスは一つとは限らないからです。上記例だと、経由するサービス群のいずれかにCFnがあれば真、という条件になります。- 「経由するサービスは一つとは限らない」例として、公式ドキュメントでは以下のような例を挙げています。
aws:CalledVia
キー値にできるサービス
サービス名 | ポリシーJSONに記載する値(サービスプリンシパル) |
---|---|
Amazon Athena | athena.amazonaws.com |
AWS CloudFormation | cloudformation.amazonaws.com |
Amazon DynamoDB | dynamodb.amazonaws.com |
AWS Key Management Service (AWS KMS) | kms.amazonaws.com |
aws:CalledViaFirst
とaws:CalledViaLast
も追加されました
aws:CalledVia
に加えて、aws:CalledViaFirst
とaws:CalledViaLast
も追加されました。これらは、CalledViaリストの最初/最後の要素を判定条件に使うものです。例えば先程のCFnとDynamoDBとKMSを使う例であれば、KMSでの暗号化権限に以下のような条件を設定した場合、結果は偽になります。上記例でのaws:CalledViaFirst
の値はCFnになるからです。
"Condition": { "StringEquals": { "aws:CalledViaFirst": [ "dynamodb.amazonaws.com" ] } }
補足: iam:PassRole
でやる方法
最初の、「CFnでVPCを作成する、でもCFn実行ユーザーにCFn外でVPCを作らせたくはない」場合はiam:PassRole
という権限を使用することでも実現可能です。
CFnスタック作成フォームの途中でIAMロールを選ぶ欄があるのをご存知でしょうか。 このIAMロールに、(CFnでの)各リソース作成を移譲する(=PassRoleする)ことができます。「スタック作成まではやるけど、その後のことはこのロールにおまかせよろしくー」みたいなイメージです。
1. リソース作成用ロールを作成する
「これまで: aws:CalledVia
が無い世界」で作成したのと同じポリシーを付与したロールを作成します。
信頼関係でCFnを設定します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "cloudformation.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
2. CFn実行用ユーザーのポリシーを修正
CFn実行用ユーザーのポリシーは、CFn全権限のAWS管理ポリシーであるAWSCloudFormationFullAccess
と、以下のインラインポリシーです。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "listrole", "Effect": "Allow", "Action": [ "iam:ListRoles" ], "Resource": "*" }, { "Sid": "passrole", "Effect": "Allow", "Action": [ "iam:PassRole" ], "Resource": "arn:aws:iam::012345678901:role/createvpc-role" } ] }
iam:ListRoles
はCFnスタック作成フォーム内でIAMロールリストを表示するのに必要です。iam:PassRole
はユーザーが IAM ロールを AWS サービスに渡すアクセス権限を定義します。今回の場合createvpc-role
というロールをCFnに渡す権限がこのユーザーに必要ですので、その設定を行なっています。(さらに厳密にやるなら、Conditionでiam:PassedToService
を使って渡すサービスをCFnに絞っても良いでしょう。(参考:AWSサービスに渡すIAMロールを制限する))
3. スタック作成
上記CFn実行用ユーザーでスタックを作成します。途中アクセス許可欄で先程作ったIAMロールを指定します。
ちなみに
ロールのポリシーにaws:CalledVia
を足してみました。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "createvpc", "Effect": "Allow", "Action": [ "ec2:DescribeVpcs", "ec2:CreateVpc", "ec2:ModifyVpcAttribute", "ec2:createTags" ], "Resource": "*", "Condition": { "ForAnyValue:StringEquals": { "aws:CalledVia": [ "cloudformation.amazonaws.com" ] } } } ] }
PassRole
した場合はパス元のaws:CalledVia
を引き継がないみたいですね。